package com.niiwoo.civet.user.service.duboo;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.niiwoo.civet.trade.dto.common.ProjectDTO;
import com.niiwoo.civet.trade.enums.ProjectFromSourceEnum;
import com.niiwoo.civet.trade.enums.ProjectSubStatusEnum;
import com.niiwoo.civet.trade.enums.TradeConfigEnum;
import com.niiwoo.civet.trade.service.configure.TradeConfigDubboService;
import com.niiwoo.civet.trade.service.loan.SpeedLoanDubboService;
import com.niiwoo.civet.trade.service.project.ProjectDubboService;
import com.niiwoo.civet.user.dao.entity.DataStatus;
import com.niiwoo.civet.user.dto.request.AppButtonDTO;
import com.niiwoo.civet.user.dto.request.JPushExtDTO;
import com.niiwoo.civet.user.dto.request.UserLetterRequestDTO;
import com.niiwoo.civet.user.dto.response.ThirdAuthResponseDTO;
import com.niiwoo.civet.user.enums.*;
import com.niiwoo.civet.user.service.UserAuthOperatorDubboService;
import com.niiwoo.civet.user.service.local.*;
import com.niiwoo.tripod.base.component.PrivacyMasks;
import com.niiwoo.tripod.service.component.SnowflakeIdWorker;
import com.niiwoo.tripod.tiancheng.component.TianchengAuthOperatorService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.Assert;
import java.util.*;

@Slf4j
@Service(version = "1.0.0")
public class UserAuthOperatorDubboServiceImpl implements UserAuthOperatorDubboService {

    @Autowired
    private TianchengAuthOperatorService tianchengAuthOperatorService;

    @Autowired
    private UserDataService userDataService;

    @Autowired
    private UserStatusService userStatusService;

    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;

    @Autowired
    PrivacyMasks privacyMasks;

    @Reference(version = "1.0.0")
    private TradeConfigDubboService tradeConfigDubboService;

    @Value(value = "${web-http-url}")
    private String webHttpUrl;

    @Reference(version = "1.0.0")
    private ProjectDubboService projectDubboService;

    /**
     * 运营商回调授权成功通知地址
     */
    private final String OPERATOR_NOTIFY_URL = "/tc/authOperatorNotify/callback";

    /**
     * 运营商获取到数据回调地址
     */
    private final String OPERATOR_CALLBACK_URL = "/tc/authOperatorCallback/callback";

    @Autowired
    private UserAuthCommonService userAuthCommonService;

    @Autowired
    private UserLetterService userLetterService;

    @Autowired
    private SendSmsLocalService sendSmsLocalService;

    @Autowired
    RedisTemplate<String, String> redisTemplate;

    @Reference(version = "1.0.0")
    private SpeedLoanDubboService speedLoanDubboService;

    /**
     * 获取天秤第三方授权h5页面的url
     *
     * @param userId   用户id
     * @param createIp 用户客户端请求ip
     * @return com.niiwoo.civet.user.dto.response.ThirdAuthResponseDTO
     * @author zzl
     * @date 2017/11/22 16:20
     */
    @Override
    public ThirdAuthResponseDTO getThirdAuthUrl(String userId, String createIp) {

        //查询授权状态
        DataStatus existsUserStatus = userStatusService.getByUserIdAndType(userId, UserDataTypeEnum.OPERATOR_AUTHORIZE);
        Byte dataStatus = null;
        if (existsUserStatus != null) {
            dataStatus = existsUserStatus.getDataStatus();
            //已授权或已验证，直接返回
            if (UserDataStatusEnum.PhoneOperatorStatus.GETSUCCESS.getStatus().equals(existsUserStatus.getDataStatus())
                    ||UserDataStatusEnum.PhoneOperatorStatus.ALREADY_CREDIT.getStatus().equals(existsUserStatus.getDataStatus())) {
                ThirdAuthResponseDTO dto = new ThirdAuthResponseDTO();
                dto.setStatus(UserDataStatusEnum.PhoneOperatorStatus.GETSUCCESS.getStatus().toString());
                dto.setUserDataStatus(dataStatus);
                return dto;
            }
        } else {
            dataStatus = UserDataStatusEnum.PhoneOperatorStatus.NOCREDIT.getStatus();
        }

        //请求天秤获取授权链接
        ThirdAuthResponseDTO dto = userAuthCommonService.getThirdAuthUrl(userId, createIp, tianchengAuthOperatorService, webHttpUrl + OPERATOR_NOTIFY_URL, webHttpUrl + OPERATOR_CALLBACK_URL, null, false, null);
        dto.setUserDataStatus(dataStatus);
        return dto;
    }

    /**
     * 通知用户重新授权【供定时任务调度】
     * <p>
     * 需求中描述：
     * 新增一条通知模板：APP极速借，系统一定时间内未获取借款人通话记录，给用户发送通知
     * 截止时间 =  计时起始时间 + 标的自动退回设置时间
     * 计时起始时间 = “最后一次授权时间”与“借款提交时间”中最新的时间
     * （截止时间，即“待审核”的极速借项目，在未成功获取到用户通话记录的情况下，标的自动退回的时间点）
     *
     * @return void
     * @author zzl
     * @date 2017/11/24 20:47
     */
    @Override
    public void notifyUserForAuthTimeout() {

        //获取数据库中指定的时长
        List<TradeConfigEnum> list = new ArrayList<>();
        list.add(TradeConfigEnum.SPEEDBORROWNOCRSENDMSGAFTERMAXMIN);
        list.add(TradeConfigEnum.SPEEDBORROWNOCRBIDPROJECTAFTERMAXMIN);
        Map<String, String> configMap = tradeConfigDubboService.selectTradeConfigByEmuns(list);
        if (configMap == null) {
            return;
        }
        //N分钟内未获取通话记录，发送通知提醒用户重新授权:的配置项key
        String key = TradeConfigEnum.SPEEDBORROWNOCRSENDMSGAFTERMAXMIN.getConfigKey();
        int minute = NumberUtils.toInt(configMap.get(key));
        if (minute <= 0) {
            return;
        }

        //查询符合条件的会员
        List<UserJoinData> userJoinDataList = joinData(minute, true);
        if(userJoinDataList == null || userJoinDataList.size() == 0){
            log.info("notifyUserReAuth查询待处理的数据为空" );
            return;
        }
        //查询退回标的的结束时间，用于计算推送消息中的取消标的时间参数
        String cancelBidMinutekey = TradeConfigEnum.SPEEDBORROWNOCRBIDPROJECTAFTERMAXMIN.getConfigKey();
        int cancelBidMinute = NumberUtils.toInt(configMap.get(cancelBidMinutekey));

        //循环执行推送消息
        for (UserJoinData userJoinData : userJoinDataList) {
            if (userJoinData == null) {
                continue;
            }
            log.info("notifyUserReAuth_UserId={}开始执行推送消息", userJoinData.userId);
            String cancelBidTimdParams = null;
            //定定时任务每10分钟执行一次
            int timerExecuteMinute = 10;
            try {
                Date paramDate = DateUtils.addMinutes(userJoinData.authDate, cancelBidMinute);
                Date upRoundDate = upRoundDate(paramDate, timerExecuteMinute);
                cancelBidTimdParams = com.niiwoo.tripod.base.utils.DateUtils.formatDate(upRoundDate, "yyyy-MM-dd HH:mm:ss");
            } catch (Throwable e) {
                log.error("notifyUserReAuth时间处理异常", e);
                continue;
            }

            if (StringUtils.isBlank(cancelBidTimdParams)) {
                continue;
            }

            //极光推送及通知
            try {
                jpushForOperatorAuthFail(userJoinData.userId, cancelBidTimdParams, userJoinData.fromSource);
            } catch (Throwable e) {
                log.error("jpushOperatorAuthFailError", e);
            }

            //发送短信
            try {
                sendSmsForInvestReceive(userJoinData.userId, cancelBidTimdParams);
            } catch (Throwable e) {
                log.error("sendMessageOperatorAuthFailError", e);
            }

            //更新状态以让用户重新授权
            try {
                DataStatus dataStatus = new DataStatus();
                dataStatus.setDataType(UserDataTypeEnum.OPERATOR_AUTHORIZE.getValue());
                dataStatus.setUserId(userJoinData.userId);
                dataStatus.setDataStatus(UserDataStatusEnum.PhoneOperatorStatus.GETFAIL.getStatus());
                userStatusService.updateDataStatusByUserIdAndType(dataStatus);
            } catch (Throwable e) {
                log.error("operatorAuthFailUpdateStatusError", e);
            }
        }

    }

    /**
     * 极光推送
     *
     * @param userId
     * @param cancelBidTimdParams
     */
    private void jpushForOperatorAuthFail(String userId, String cancelBidTimdParams, Byte projectFromSource) {

        List<Object> listForLetter = new ArrayList<>();
        listForLetter.add(cancelBidTimdParams);

        UserLetterRequestDTO letterRequestDTO = new UserLetterRequestDTO();
        letterRequestDTO.setToUserId(userId);
        //letterRequestDTO.setUserLetterTypeEnum(UserLetterTypeEnum.LETTER_AND_JPUSH);
        letterRequestDTO.setJpushTemplateKeyEnum(MessageTemplateKeyEnum.JPushTemplateKeyEnum.SPEED_OPERATOR_AUTH_FAIL);
        letterRequestDTO.setJpushTemplateParamList(listForLetter);

        //点击“重新授权”，跳转到App极速借-手机运营商授权页面；
        //点击“借款详情”，跳转到App我的借款列表（进行中）页面；
        JPushExtDTO jPushExtDTO = new JPushExtDTO();
        jPushExtDTO.setRedirectPageEnum(JPushEnum.RedirectPageEnum.SPEED_LOAN_HOME_PAGE);
        letterRequestDTO.setJPushExt(jPushExtDTO);

        //App申请的发送jpush
        if (ProjectFromSourceEnum.APP.getType().equals(projectFromSource)) {
            userLetterService.jpush(letterRequestDTO);
        }

        //发送站内信
        letterRequestDTO.setLetterTemplateEnum(MessageTemplateKeyEnum.LetterTemplateKeyEnum.SPEED_OPERATOR_AUTH_FAIL);
        letterRequestDTO.setLetterTemplateParamList(listForLetter);
        //设置站内信按钮
        AppButtonDTO reAuthButtonDTO = new AppButtonDTO();
        reAuthButtonDTO.setButtonType(AppButtonDTO.ButtonTypeEnum.REGAIN_ACCREDIT);
        String buttonLabel = "重新授权";
        reAuthButtonDTO.setLabel(buttonLabel);

        AppButtonDTO loanButtonDTO = new AppButtonDTO();
        loanButtonDTO.setButtonType(AppButtonDTO.ButtonTypeEnum.LOAN_ING_LIST);
        String loanButtonLabel = "借款详情";
        loanButtonDTO.setLabel(loanButtonLabel);

        List<AppButtonDTO> listForButton = new ArrayList<>();
        listForButton.add(reAuthButtonDTO);
        listForButton.add(loanButtonDTO);
        letterRequestDTO.setLetterButtonList(listForButton);
        userLetterService.pushLetter(letterRequestDTO);

    }

    /**
     * 发送短信
     *
     * @param endTime
     */
    private void sendSmsForInvestReceive(String userId, String endTime) {
        Assert.notNull(endTime, "超时退回标的提醒的时间不能为空");

        List<Object> listForSms = new ArrayList<>();
        listForSms.add(endTime);

        sendSmsLocalService.sendSmsByUserId(userId, SMSTemplateKeyEnum.TEMP_OPERATOR_AUTH_FAIL.getKey(), listForSms);
    }

    /**
     * 系统自动审核并退回借款标【供定时任务调度】
     *
     * @return void
     * @author zzl
     * @date 2017/11/24 20:47
     */
    @Override
    public void returnProjectForAuthTimeout() {
        //获取数据库中指定的时长
        List<TradeConfigEnum> list = new ArrayList<>();
        list.add(TradeConfigEnum.SPEEDBORROWNOCRBIDPROJECTAFTERMAXMIN);
        Map<String, String> configMap = tradeConfigDubboService.selectTradeConfigByEmuns(list);
        if (configMap == null) {
            return;
        }
        //N分钟内未获取通话记录，系统自动审核并退回借款标:的配置项key
        String key = TradeConfigEnum.SPEEDBORROWNOCRBIDPROJECTAFTERMAXMIN.getConfigKey();
        int minute = NumberUtils.toInt(configMap.get(key));

        if (minute <= 0) {
            return;
        }

        //查询数据
        List<UserJoinData> userJoinDataList = joinData(minute, false);

        if(userJoinDataList == null || userJoinDataList.size() == 0){
            log.info("returnProjectForAuthTimeout查询待处理的数据为空" );
            return;
        }

        //循环执行退回标的
        for (UserJoinData userJoinData : userJoinDataList) {
            if (userJoinData == null) {
                continue;
            }
            log.info("returnProjectForAuthTimeout_UserId={}开始执行推送消息", userJoinData.userId);

            //退回标的
            try {
                speedLoanDubboService.returnProject(userJoinData.projectId, ProjectSubStatusEnum.CHECKING_BACK_PHONEOPERATOR_TIMEOUT);
            }catch (Exception e){
                log.error("AuthOperatorReturnProjectError", e);
            }
        }
    }

    private List<UserJoinData> joinData(int minute, boolean isNotify) {

        //本次查询结束时间
        Date endDate = DateUtils.addMinutes(new Date(), -minute);
        log.info("userAuthOperatorJoinData_{} 查询截止时间: {}",isNotify ,endDate);
        //查询待审核借款标数据
        List<ProjectDTO> projectDTOList = projectDubboService.queryAuthNotifyAuditProjectList(endDate);

        if (projectDTOList == null) {
            log.info("userAuthOperatorJoinData查询返回列表为空，类型isNotify：{}", isNotify);
            return null;
        }

        log.info("userAuthOperatorJoinData_{}查询返回列表长度为：{}", isNotify, projectDTOList.size());

        if(projectDTOList.size() == 0){
            return null;
        }

        List<UserJoinData> userJoinDataList = new ArrayList<>(projectDTOList.size());

        for (ProjectDTO projectDTO : projectDTOList) {
            DataStatus dataStatus = userStatusService.getByUserIdAndType(projectDTO.getBorrowUserId(), UserDataTypeEnum.OPERATOR_AUTHORIZE);
            if (dataStatus == null) {
                log.info("userAuthOperatorJoinData_{}查询DataStatus数据为空,userId:{}", isNotify, projectDTO.getBorrowUserId());
                continue;
            }
            if (dataStatus.getAuthTime() == null) {
                log.info("userAuthOperatorJoinData_{}查询DataStatus授权时间为空，userId:{}", isNotify, projectDTO.getBorrowUserId());
                continue;
            }

            //如果在查询时间之后重新授权了,不处理，直接返回
            if (dataStatus.getAuthTime().getTime() > endDate.getTime()
                    && UserDataStatusEnum.PhoneOperatorStatus.ALREADY_CREDIT.getStatus().equals(dataStatus.getDataStatus())) {
                log.info("userAuthOperatorJoinData_{}查询DataStatus状态为重新授权，userId:{}，authTime:{}, endDate:{}", isNotify, projectDTO.getBorrowUserId(), dataStatus.getAuthTime(), endDate);
                continue;
            }

            //已经授权成功：
            if(UserDataStatusEnum.PhoneOperatorStatus.GETSUCCESS.getStatus().equals(dataStatus.getDataStatus())){
                log.info("userAuthOperatorJoinData_{}查询DataStatus状态为已经授权成功,userId:{}", isNotify, projectDTO.getBorrowUserId());
                continue;
            }

            //如果是发送消息，判断已经修改状态就不再发送
            if(isNotify){
                if(UserDataStatusEnum.PhoneOperatorStatus.GETFAIL.getStatus().equals(dataStatus.getDataStatus())){
                    log.info("userAuthOperatorJoinData_{}查询DataStatus状态为已修改为授权失败，不再发送消息，userId:{}", isNotify, projectDTO.getBorrowUserId());
                    continue;
                }
            }

            Date maxDate = projectDTO.getSubmitTime();
            if (dataStatus.getAuthTime().getTime() > maxDate.getTime()) {
                maxDate = dataStatus.getAuthTime();
            }

            UserJoinData userJoinData = new UserJoinData();
            userJoinData.userId = projectDTO.getBorrowUserId();
            userJoinData.authDate = maxDate;
            userJoinData.projectId = projectDTO.getProjectId();
            userJoinData.fromSource = projectDTO.getFromSource();

            userJoinDataList.add(userJoinData);
            log.info("userAuthOperatorJoinData_{}userId:{}添加到待处理集合", isNotify, projectDTO.getBorrowUserId());
        }
        return userJoinDataList;
    }

    /**
     * 数据合并的数据对象
     */
    private class UserJoinData {
        String userId;
        Long projectId;
        Date authDate;
        /**
         * 标的申请来源（1、APP，2、H5活动，3、H5极速借, 4、融360）
         */
        Byte fromSource;
    }

    /**
     * 向上取整分钟数，
     * 如当前时间是2018-01-16 17:01:34 ,若truncateMin=10,则结果是2018-01-16 17:10:00
     * 若truncateMin=5,则结果是2018-01-16 17:05:00
     * @param date
     * @param truncateMin
     * @return
     */
    private static Date upRoundDate(Date date, int truncateMin){
        if(date == null){
            return null;
        }
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        int truncateMinute = (c.get(Calendar.MINUTE)/truncateMin)*truncateMin;
        c.set(Calendar.MINUTE, truncateMinute);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        c.add(Calendar.MINUTE, truncateMin);
        return c.getTime();
    }

}
